package com.kiseny.dao;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.common.intface.IBaseDAO;
import com.kiseny.po.BasePO;

public class BaseDAO<T extends BasePO> extends HibernateDaoSupport implements
    IBaseDAO<T> {
	
    private Logger logger = (Logger) LogManager.getLogger(this.getClass().getName());
    
    public Logger getLogger() {
        return logger;
    }

    public void setLogger(Logger logger) {
        this.logger = logger;
    }
    
    private Class<T> persistentClass;
    protected static final boolean DELETED_NEED = false;
    
    
    /**
     * @return the persistentClass
     */
    public Class<T> getPersistentClass() {
        return this.persistentClass;
    }

    @SuppressWarnings("unchecked")
    public BaseDAO() {
        Type t = this.getClass().getGenericSuperclass();

        if (t instanceof Class) {
            this.persistentClass = (Class<T>) t.getClass();
        } else if (t instanceof ParameterizedType) {
            this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                    .getGenericSuperclass()).getActualTypeArguments()[0];
        } else {
            throw new RuntimeException("Can not handle type construction for '"
                    + getClass() + "'!");
        }
    }

    public T save(T entity) {
        this.getHibernateTemplate().saveOrUpdate(entity);
        return entity;
    }

    public void delete(T entity) {
        this.getHibernateTemplate().delete(entity);
    }

    public T update(T entity) {
        this.getHibernateTemplate().update(entity);
        return entity;
    }

	public T find(int id) {

        T entity = null;
        try {

            entity = (T) this.getHibernateTemplate().get(this.getPersistentClass(), id);
        } catch (RuntimeException e) {
            throw e;
        }

        return entity;
    }

    @SuppressWarnings("unchecked")
    public List<T> findByCriteria(Criterion... criterion) {
        DetachedCriteria detachedCrit = DetachedCriteria
                .forClass(getPersistentClass());
        for (Criterion c : criterion) {
            detachedCrit.add(c);
        }

        return getHibernateTemplate().findByCriteria(detachedCrit);
    }
    
    @SuppressWarnings("unchecked")
    public List<T> findByCriteria(DetachedCriteria detachedCrit) {
        return getHibernateTemplate().findByCriteria(detachedCrit);
    }

    @SuppressWarnings("unchecked")
    public List<T> findAll() {
        List<T> list = new ArrayList<T>();
        DetachedCriteria criteria = DetachedCriteria.forClass(this.getPersistentClass());
        try {
            list = (List<T>) getHibernateTemplate().findByCriteria(criteria);
        } catch (RuntimeException e) {
            throw e;
        }
        return list;
    }
    

    @SuppressWarnings({ "unchecked", "rawtypes" })
	public Long totalNumber() {
        StringBuilder hql = new StringBuilder().append(
                "select count(obj.id) from ").append(
                BaseDAO.this.getPersistentClass().getSimpleName()).append(
                " obj ");
        if (BaseDAO.DELETED_NEED) {
            //Empty;
        } else {
            hql.append("where obj.deleteFlag = 0");
        }
        
        final String hqlString = hql.toString();
        return (Long) this.getHibernateTemplate().execute(
                new HibernateCallback() {
                    public Object doInHibernate(Session session) throws HibernateException, SQLException {
                        Query query = session.createQuery(hqlString.toString());
                        return query.uniqueResult();
                    }
                });
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public List<T> listAndSort(int startPos, int amount,
            Map<String, String> conditions, String sortableProperty, boolean asc) {

        final Map<String, String> conditionsN = new HashMap<String, String>();
        conditionsN.putAll(conditions);
        final int startPosN = startPos;
        final int amountN = amount;
        
        boolean hasCondition = false;   
        
        StringBuilder hql = new StringBuilder().append("from ").append(
                this.getPersistentClass().getSimpleName()).append(" obj ");   
            
        //Whether search the data, which has been deleted.
        if (BaseDAO.DELETED_NEED) {   
            //Empty
        } else {
            hasCondition = true;   
            hql.append("where obj.deleteFlag = 0 ");
        }
            
        //set condition   
        boolean needsAnd = false;   
        if (!conditionsN.isEmpty()) {   
            if (!hasCondition) {   
                hql.append("where ");   
            } else {   
                needsAnd = true;   
            }   
        }   
        for (String property : conditionsN.keySet()) {   
            if (needsAnd) {   
                hql.append("and ");   
            }   
            hql.append("obj.").append(property).append(" = :").append(property).append(" ");   
        }   
            
        //By order
        hql.append("order by ").append(" obj.").append(sortableProperty).append(" ");   
        if (asc) {   
            hql.append("asc");   
        } else {   
            hql.append("desc");   
        }   
            
        //pagination
        final String hqlString = hql.toString();

        return (List<T>) this.getHibernateTemplate().execute(new HibernateCallback() {     
            public Object doInHibernate(Session session) throws HibernateException, SQLException {   
                Query query = session.createQuery(hqlString);   
                query.setFirstResult(startPosN);   
                query.setMaxResults(amountN);   
                    
                //fill condition
                for (String property : conditionsN.keySet()) {   
                    query.setParameter(property, conditionsN.get(property));   
                }   
                    
                return query.list();   
            }   
        });   
    }

    
    
    
    @SuppressWarnings("unchecked")
    public List<T> findByCriteria(DetachedCriteria detachedCrit, int maxResult) {
        getHibernateTemplate().setMaxResults(maxResult);
        return getHibernateTemplate().findByCriteria(detachedCrit);
    }
    
}